package net.grapsus.arduino;

/* file name  : SketchClassLoader.java
 * authors    : Mihai MORARU
 * created    : Mon Jun  6 00:25:17 2011
 * copyright  : 
 *
 * modifications:
 *
 */

import java.io.*;
import net.grapsus.arduino.ArduinoCompiler;

public class SketchClassLoader extends ClassLoader {
	// Compile a sketch file using ANTLR+ASM
	
	public Class findClass(String name) throws ClassNotFoundException
	{
		System.out.println("findClass("+name+")");
		if(!name.endsWith(".pde"))
			throw new ClassNotFoundException(name);
		File file = new File(name);
		if(!file.exists())
			throw new ClassNotFoundException(name);
		String baseName = file.getName();
		String className = baseName;
		int i = baseName.lastIndexOf(".");
		if(i > 0)
			className = baseName.substring(0, i);
		className = Character.toUpperCase(className.charAt(0)) + className.substring(1);
		//String javaName = file.getParent()+className+".java";
		String javaName = className+".java";

		// sketch -> class 
		ArduinoCompiler compiler = new ArduinoCompiler();
		try {
			compiler.parseFile(name);
			byte[] classData = null;
			try {
				classData = compiler.classGen(className);
			}
			catch(Exception e)
			{
				System.out.println("Error compiling sketch : " + e.getMessage());
				e.printStackTrace();
			}
			// DEBUG
			FileOutputStream fos = new FileOutputStream("Debug.class");
			fos.write(classData);
			fos.close();
			return defineClass(null, classData, 0, classData.length);
		} catch(IOException e) {
			throw new ClassNotFoundException("ArduinoCompiler failed");
		}

		//return findSystemClass(className);
	}
	
	// Compile a sketch file using sorcery and javac
	/*
	public Class findClass(String name) throws ClassNotFoundException
	{
		System.out.println("findClass("+name+")");
		if(!name.endsWith(".pde"))
			throw new ClassNotFoundException(name);
		File file = new File(name);
		if(!file.exists())
			throw new ClassNotFoundException(name);
		String baseName = file.getName();
		String className = baseName;
		int i = baseName.lastIndexOf(".");
		if(i > 0)
			className = baseName.substring(0, i);
		className = Character.toUpperCase(className.charAt(0)) + className.substring(1);
		//String javaName = file.getParent()+className+".java";
		String javaName = className+".java";

		// sketch -> java
		try {
			sketch2java(className, name, javaName);
		} catch(IOException e) {
			throw new ClassNotFoundException(name);
		}
		// java -> class
		try {
			if(!compile(javaName))
				throw new ClassNotFoundException("Failed to compile "+name);
		} catch(IOException e) {
			throw new ClassNotFoundException(name);
		}
		// Load class
		byte[] b;
		try {
			b = getBytes(className+".class");
		} catch(IOException e) {
			throw new ClassNotFoundException(name);
		}

		return defineClass(null, b, 0, b.length);
		//return findSystemClass(className);
	}
	*/

	private void sketch2java(String name, String fin, String fout) throws IOException
	{
		System.out.println("sketch2java("+name+", "+fin+", "+fout+")");
		FileOutputStream fos = new FileOutputStream(fout);
		byte[] sketch = getBytes(fin);
		String header = "public class "+name+" extends net.grapsus.arduino.ArduinoCore {\n";
		String footer = "}";
		fos.write(header.getBytes());
		fos.write(sketch);
		fos.write(footer.getBytes());
		fos.close();
	}

	private static int exec(String command) throws IOException
	{
		System.out.println("exec("+command+")");
		Process p = Runtime.getRuntime().exec(command);
		try {
			p.waitFor();
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
		return p.exitValue();
	}

	private static int exec(String[] command) throws IOException
	{
		System.out.println("exec("+command+")");
		Process p = Runtime.getRuntime().exec(command);
		try {
			p.waitFor();
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
		return p.exitValue();
	}

	private boolean compile(String javaFile) throws IOException
	{
		System.out.println("javac "+javaFile);
		//exec("/bin/sed -i -e s_\\<unsigned\\>__ -e s_\\<const\\>__ "+javaFile);
		String[] commandline = new String[] {
			"/bin/sed",
			"-i",
			"-e s_\\<unsigned\\>__",
			"-e s_\\<const\\>__",
			"-e s_void setup_protected void setup_",
			"-e s_void loop_protected void loop_",
			javaFile
		};
		exec(commandline);
		int ret = exec("javac "+javaFile);
		return ret == 0;
	}

	private byte[] getBytes(String name) throws IOException
	{
		// Find out the length of the file
		File file = new File(name);
		long len = file.length();
		// Reserver memory
		byte[] raw = new byte[(int)len];
		// Open the file
		FileInputStream fin = new FileInputStream(file);
		// Read everything
		if(fin.read(raw) != len)
			throw new IOException("Can't read all");
		fin.close();

		return raw;
	}
}
